
/* DEV NOTE: If you start seeing errors in this class, it may be that there was code
 * that did not sync properly with code from DHS when this was merged. Check the
 * archives if you have problems. 2/15/09. -TC
 */

namespace Behemoth.SharePoint.CustomFieldTypes {
    using System;
    using System.Collections.Generic;
    using System.Text;

    using Microsoft.SharePoint;

    using Behemoth.Text;
    using Behemoth.Xml.Serialization;

  public enum XmlFieldValueIndexes {
    /// <summary>
    /// Represents the Base64 encoded string of serializable XML that stores the object.
    /// </summary>
    Base64EncodedXml = 0,
    /// <summary>
    /// Represents a summary view of the displayable text that represents what is stored in Xml when CAML is being used to display the fieldName.
    /// </summary>
    DisplayValueSummary = 1,
    /// <summary>
    /// Represents a full view of the displayable text that represents what is stored in Xml when CAML is being used to display the fieldName.
    /// </summary>
    DisplayValueFull = 2
  }

  public abstract class XmlFieldValue : SPFieldMultiColumnValue {

    /// <summary>
    /// Override this abstract method to provide logic in your class to create
    /// a new instance of your serializable data object.
    /// </summary>
    /// <returns></returns>
    public abstract object CreateSerializableDataObject();

    /// <summary>
    /// Override this abstract method to provide logic in your class to set
    /// the full and summary display values based on the data in your Data object.
    /// </summary>
    public abstract void SyncDisplayValues();

    protected const int NUM_SUBCOLUMNS = 3;

    #region Properties

    protected static Base64Encoder _encoder = new Base64Encoder();
    private Serializer _serializer;
    protected Serializer serializer {
      get {
        if (_serializer == null) {
          Type t = CreateSerializableDataObject().GetType();
          _serializer = new Serializer(t);
          _serializer.EncodingMethod = _encoder.Encoder;
        }
        return _serializer;
      }
    }

    protected virtual int NumSubColumns {
      get { return NUM_SUBCOLUMNS + _additionalSubColumns; }
    }
    private int _additionalSubColumns = 0;

    public string this[XmlFieldValueIndexes index] {
      get { return base[(int)index]; }
      set { base[(int)index] = value; }
    }

    private object _data;
    public object Data {
      get {
        if (this.DataIsEmpty)
          _data = CreateSerializableDataObject();
        return _data; 
      }
      //set { _data = value; }
    }

    /// <summary>
    /// Provides a low impact way to test if Data exists
    /// without creating a new Data object.
    /// </summary>
    public bool DataIsEmpty {
      get {
        return (this._data == null);
      }
    }

    private string _displayValueSummary;
    public string DisplayValueSummary {
      get { return _displayValueSummary; }
      set { _displayValueSummary = value; }
    }

    private string _displayValueFull;
    public string DisplayValueFull {
      get { return _displayValueFull; }
      set { _displayValueFull = value; }
    }

    #endregion

    #region Constructors

    public XmlFieldValue( string multiColumnValue )
			: base( multiColumnValue ) {
			if (this.Count != this.NumSubColumns )
				throw new IndexOutOfRangeException( 
          string.Format(
            "The string passed to {0} had the wrong number of sub-columns. The correct number is {1} but there were actually {2}. ",
            this.GetType().Name, 
            this.NumSubColumns, 
            this.Count));
      GetSubColumns(multiColumnValue);
		}
    public XmlFieldValue(string displayValueFull, string displayValueSummary, object data, int numAdditionalSubColumns)
      : base(NUM_SUBCOLUMNS + numAdditionalSubColumns) { // this.NumSubColumns
      this._additionalSubColumns = numAdditionalSubColumns;
      this._data = data;
      this._displayValueFull = displayValueFull;
      this._displayValueSummary = displayValueSummary;
    }

    #endregion

    public override string ToString() {
      UpdateSubColumns();
      return base.ToString();
    }

    protected virtual void GetSubColumns(string multiColumnValue) {
      // copy xml data
      string base64 = this[XmlFieldValueIndexes.Base64EncodedXml];
      if (!string.IsNullOrEmpty(base64)) {
        // decode the base 64 string
        string xml = _encoder.DecodeXmlData(base64);
        // turn the Xml back into an object
        object o = serializer.Deserialize(xml);
        this._data = o;
      }
      // copy other basic properties
      this._displayValueFull = this[XmlFieldValueIndexes.DisplayValueFull];
      this._displayValueSummary = this[XmlFieldValueIndexes.DisplayValueSummary];
    }
    protected virtual void UpdateSubColumns() {
      // set the summary and full text using the abstract method
      SyncDisplayValues();
      // copy xml data
      string xmlData = string.Empty;
      if (!this.DataIsEmpty) {
        object o = this.Data;
        // serialize the data object
        xmlData = serializer.Serialize(o);
      }
      this[XmlFieldValueIndexes.Base64EncodedXml] = _encoder.EncodeXmlData(xmlData);
      // copy other basic properties
      this[XmlFieldValueIndexes.DisplayValueFull] = this._displayValueFull;
      this[XmlFieldValueIndexes.DisplayValueSummary] = this._displayValueSummary;
    }

    public virtual void ClearSubColumns() {
      this._data = null;
      this._displayValueFull = string.Empty;
      this._displayValueSummary = string.Empty;
    }

    public virtual bool IsValid() {
      return true;
    }
    private List<string> _validationMessages = new List<string>();
    public List<string> ValidationMessages {
      get {
        return _validationMessages;
      }
    }

  } // class

} // namespace
